# vim: set et sw=4 sts=4 fileencoding=utf-8:
#
# Python camera library for the Rasperry-Pi camera module
# Copyright (c) 2013-2017 Dave Jones <dave@waveform.org.uk>
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are met:
#
#     * Redistributions of source code must retain the above copyright
#       notice, this list of conditions and the following disclaimer.
#     * Redistributions in binary form must reproduce the above copyright
#       notice, this list of conditions and the following disclaimer in the
#       documentation and/or other materials provided with the distribution.
#     * Neither the name of the copyright holder nor the
#       names of its contributors may be used to endorse or promote products
#       derived from this software without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE.

from __future__ import (
    unicode_literals,
    print_function,
    division,
    absolute_import,
    )

# Make Py2's str and range equivalent to Py3's
str = type('')

import warnings
from collections import namedtuple

from picamera.exc import (
    mmal_check,
    PiCameraError,
    PiCameraMMALError,
    PiCameraValueError,
    PiCameraRuntimeError,
    PiCameraDeprecated,
    )


class PiVideoFrameType(object):
    """
    This class simply defines constants used to represent the type of a frame
    in :attr:`PiVideoFrame.frame_type`. Effectively it is a namespace for an
    enum.

    .. attribute:: frame

        Indicates a predicted frame (P-frame). This is the most common frame
        type.

    .. attribute:: key_frame

        Indicates an intra-frame (I-frame) also known as a key frame.

    .. attribute:: sps_header

        Indicates an inline SPS/PPS header (rather than picture data) which is
        typically used as a split point.

    .. attribute:: motion_data

        Indicates the frame is inline motion vector data, rather than picture
        data.

    .. versionadded:: 1.5
    """
    frame = 0
    key_frame = 1
    sps_header = 2
    motion_data = 3


class PiVideoFrame(namedtuple('PiVideoFrame', (
    'index',         # the frame number, where the first frame is 0
    'frame_type',    # a constant indicating the frame type (see PiVideoFrameType)
    'frame_size',    # the size (in bytes) of the frame's data
    'video_size',    # the size (in bytes) of the video so far
    'split_size',    # the size (in bytes) of the video since the last split
    'timestamp',     # the presentation timestamp (PTS) of the frame
    'complete',      # whether the frame is complete or not
    ))):
    """
    This class is a :func:`~collections.namedtuple` derivative used to store
    information about a video frame. It is recommended that you access the
    information stored by this class by attribute name rather than position
    (for example: ``frame.index`` rather than ``frame[0]``).

    .. attribute:: index

        Returns the zero-based number of the frame. This is a monotonic counter
        that is simply incremented every time the camera starts outputting a
        new frame. As a consequence, this attribute cannot be used to detect
        dropped frames. Nor does it necessarily represent actual frames; it
        will be incremented for SPS headers and motion data buffers too.

    .. attribute:: frame_type

        Returns a constant indicating the kind of data that the frame contains
        (see :class:`PiVideoFrameType`). Please note that certain frame types
        contain no image data at all.

    .. attribute:: frame_size

        Returns the size in bytes of the current frame. If a frame is written
        in multiple chunks, this value will increment while :attr:`index`
        remains static. Query :attr:`complete` to determine whether the frame
        has been completely output yet.

    .. attribute:: video_size

        Returns the size in bytes of the entire video up to this frame.  Note
        that this is unlikely to match the size of the actual file/stream
        written so far. This is because a stream may utilize buffering which
        will cause the actual amount written (e.g. to disk) to lag behind the
        value reported by this attribute.

    .. attribute:: split_size

        Returns the size in bytes of the video recorded since the last call to
        either :meth:`~PiCamera.start_recording` or
        :meth:`~PiCamera.split_recording`. For the reasons explained above,
        this may differ from the size of the actual file/stream written so far.

    .. attribute:: timestamp

        Returns the presentation timestamp (PTS) of the frame. This represents
        the point in time that the Pi received the first line of the frame from
        the camera.

        The timestamp is measured in microseconds (millionths of a second).
        When the camera's clock mode is ``'reset'`` (the default), the
        timestamp is relative to the start of the video recording.  When the
        camera's :attr:`~PiCamera.clock_mode` is ``'raw'``, it is relative to
        the last system reboot. See :attr:`~PiCamera.timestamp` for more
        information.

        .. warning::

            Currently, the camera occasionally returns "time unknown" values in
            this field. In this case, picamera will simply re-use the timestamp
            of the previous frame (under the assumption that time never goes
            backwards). This happens for SPS header "frames", for example.

    .. attribute:: complete

        Returns a bool indicating whether the current frame is complete or not.
        If the frame is complete then :attr:`frame_size` will not increment
        any further, and will reset for the next frame.

    .. versionchanged:: 1.5
        Deprecated :attr:`header` and :attr:`keyframe` attributes and added the
        new :attr:`frame_type` attribute instead.

    .. versionchanged:: 1.9
        Added the :attr:`complete` attribute.
    """

    __slots__ = () # workaround python issue #24931

    @property
    def position(self):
        """
        Returns the zero-based position of the frame in the stream containing
        it.
        """
        return self.split_size - self.frame_size

    @property
    def keyframe(self):
        """
        Returns a bool indicating whether the current frame is a keyframe (an
        intra-frame, or I-frame in MPEG parlance).

        .. deprecated:: 1.5
            Please compare :attr:`frame_type` to
            :attr:`PiVideoFrameType.key_frame` instead.
        """
        warnings.warn(
            PiCameraDeprecated(
                'PiVideoFrame.keyframe is deprecated; please check '
                'PiVideoFrame.frame_type for equality with '
                'PiVideoFrameType.key_frame instead'))
        return self.frame_type == PiVideoFrameType.key_frame

    @property
    def header(self):
        """
        Contains a bool indicating whether the current frame is actually an
        SPS/PPS header. Typically it is best to split an H.264 stream so that
        it starts with an SPS/PPS header.

        .. deprecated:: 1.5
            Please compare :attr:`frame_type` to
            :attr:`PiVideoFrameType.sps_header` instead.
        """
        warnings.warn(
            PiCameraDeprecated(
                'PiVideoFrame.header is deprecated; please check '
                'PiVideoFrame.frame_type for equality with '
                'PiVideoFrameType.sps_header instead'))
        return self.frame_type == PiVideoFrameType.sps_header
